package com.cat2bug.junit.clazz;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.List;
import java.util.Map;

import com.cat2bug.junit.service.ParameterService;
import com.cat2bug.junit.util.JavassistUtils;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.MemberValue;

/**
 * 添加类方法
 * @author yuzhantao
 *
 */
public abstract class AbstractAddMethodOfTestClass extends AbstractTestClassDecorator {
	protected Class<?>[] paramClasses;
	protected String name;
	protected Map<Class<? extends Annotation>, Map<String, Object>> annotations;

	protected CtClass returnClasses;

	public AbstractAddMethodOfTestClass(ITestClassFactory factory,String name) {
		this(factory,name,null,null);
	}

	public AbstractAddMethodOfTestClass(
			ITestClassFactory factory,
			String name,
			Class<?>[] paramesClasses,
			Map<Class<? extends Annotation>, Map<String, Object>> annotations) {
		this(factory,name,paramesClasses,annotations,CtClass.voidType);
	}

	public CtClass getReturnClasses() throws ClassNotFoundException, NotFoundException {
		return this.returnClasses;
	}

	public AbstractAddMethodOfTestClass(
			ITestClassFactory factory,
			String name,
			Class<?>[] paramClasses,
			Map<Class<? extends Annotation>,
			Map<String, Object>> annotations,
			CtClass returnClasses) {
		super(factory);
		this.name=name;
		this.paramClasses = paramClasses;
		this.annotations=annotations;
		this.returnClasses = returnClasses;
	}

	@Override
	public CtClass createTestClass(Class<?> clazz) throws Exception {
		CtClass ctClass = super.createTestClass(clazz);
		ClassFile classFile = ctClass.getClassFile();
		ConstPool constpool = classFile.getConstPool();
		CtClass[] parameCtClass = new CtClass[0];

		ClassPool cp = ClassPool.getDefault();
		// 添加参数
		if(this.paramClasses !=null) {
			parameCtClass = new CtClass[this.paramClasses.length];
			for(int i = 0; i<this.paramClasses.length; i++) {
				parameCtClass[i]=cp.makeClass(this.paramClasses[i].getName());
			}
		}
		CtMethod method = new CtMethod(getReturnClasses(), this.name, parameCtClass, ctClass); //  创建方法对象
		// 添加方法注解
		if (this.annotations != null) {
			AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
			for (Map.Entry<Class<? extends Annotation>, Map<String, Object>> a : this.annotations.entrySet()) {
				javassist.bytecode.annotation.Annotation annotation = new javassist.bytecode.annotation.Annotation(
						a.getKey().getName(), constpool);
				// 添加注解参数
				if (a.getValue() != null) {
					for (Map.Entry<String, Object> param : a.getValue().entrySet()) {
						if (param.getValue().getClass().isArray()) {
							int len = Array.getLength(param.getValue());
							MemberValue[] mvs = new MemberValue[len];
							for (int i = 0; i < len; i++) {
								mvs[i] = JavassistUtils.obj2MemberObj(param.getValue(), constpool);
							}
							ArrayMemberValue amv = new ArrayMemberValue(constpool);
							amv.setValue(mvs);
							annotation.addMemberValue(param.getKey(), amv);
						} else {
							annotation.addMemberValue(param.getKey(),
									JavassistUtils.obj2MemberObj(param.getValue(), constpool));
						}
					}
				}
				attr.addAnnotation(annotation);
			}
			method.getMethodInfo().addAttribute(attr);
		}
		String body = this.body(ctClass);
		method.setBody(body);
		ctClass.addMethod(method);
		return ctClass;
	}

	public abstract String body(CtClass ctClass) throws Exception;
}
